/**
 * @file STL_CURL.cpp
 * @author Derek Liu (yjkhtddx@sina.com)
 * @brief 
 * @version 0.1
 * @date 2021-01-08
 * 
 * @copyright Copyright Sci-Tech Laboratories (c) 2021
 * 
 */

#include "STL_CURL.h"
#include <curl/curl.h>
#include <iostream>
#include <STLOG.h>
#include <string.h>
#include "STL_Json.h"
#include <stdlib.h> //realloc
#include <memory>   //std::unique_ptr

namespace STL
{
    STL_CURL::STL_CURL() : STL_ModLog("STL_CURL"), m_dns("")
    {
    }

    STL_CURL *STL_CURL::getInstance()
    {
        static STL_CURL instance;
        return &instance;
    }

    size_t STL_CURL::write_function_callback(void *data, size_t size, size_t nmemb, void *userp)
    {
        size_t realsize = size * nmemb;
        CurlMemory *mem = (CurlMemory *)userp;

        char *ptr = (char *)realloc((void *)(mem->response), mem->size + realsize + 1); // 注意:realloc第一个参数为空或者内存空间,注意初始化
        if (ptr == NULL)
            return 0; /* out of memory! */

        mem->response = ptr;
        memcpy(&(mem->response[mem->size]), data, realsize);
        mem->size += realsize;
        mem->response[mem->size] = 0;

        return realsize;
    }

    std::string STL_CURL::stl_http_get(std::string url, std::string codeFile, int codeLine, bool isLog /* = true*/)
    {
        HttpReqest req;
        req.url = url;
        req.codeFile = codeFile;
        req.codeLine = codeLine;
        req.isLog = isLog;
        return stl_http_get(req);
    }
    std::string STL_CURL::stl_http_get(HttpReqest req)
    {
        std::string ret = "";
        CurlMemory chunk;

        CURL *curl = NULL;
        CURLcode res;
        char err_msg[CURL_ERROR_SIZE + 1] = {0};
        curl = curl_easy_init();
        curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
        curl_easy_setopt(curl, CURLOPT_URL, (char *)req.url.c_str());
        curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);
        curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &STL_CURL::write_function_callback);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)(&chunk));
        curl_easy_setopt(curl, CURLOPT_TIMEOUT, req.timeout);
        curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 15);
        curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
        curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, err_msg);
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
        if (!m_dns.empty())
        {
            //https://curl.se/libcurl/c/CURLOPT_DNS_SERVERS.html
            curl_easy_setopt(curl, CURLOPT_DNS_SERVERS, m_dns.c_str());
        }
        res = curl_easy_perform(curl);
        if (CURLE_OK != res)
        {
            int res_code = 0;
            res = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &res_code); //获得http返回的状态码
            curl_easy_cleanup(curl);
            if (req.isLog)
                stl_modlog(STL::STL_ModLog::STL_ModLog_Info, "\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n[GET] %s\n--------------------------------\ncurl_easy_perform failed: [CURLcode:%d][code:%d](err_msg:%s)\n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n", req.codeFile.c_str(), req.codeLine, __FUNCTION__, req.url.c_str(), res, res_code, err_msg);
            return ret;
        }
        else
        {
            int res_code = 0;
            ret = std::string(chunk.response, chunk.size);
            res = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &res_code); //获得http返回的状态码
            if (req.isLog)
                stl_modlog(STL::STL_ModLog::STL_ModLog_Info, "\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n[GET] %s\n--------------------------------\n[code:%d][len:%zu]\n%s\n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n", req.codeFile.c_str(), req.codeLine, __FUNCTION__, req.url.c_str(), res_code, chunk.size, ret.c_str() /*ret.substr(0, 1000).c_str()*/);
            curl_easy_cleanup(curl);
            return ret;
        }
        return ret;
    }

    std::string STL_CURL::stl_http_post(std::string url, std::string body, std::string codeFile, int codeLine, bool isLog)
    {
        HttpReqest req;
        req.url = url;
        req.body = body;
        req.codeFile = codeFile;
        req.codeLine = codeLine;
        req.isLog = isLog;
        return stl_http_post(req);
    }

    std::string STL_CURL::stl_http_post(HttpReqest req)
    {
        // curl_global_init(CURL_GLOBAL_ALL);
        // stl_modlog(STL::STL_ModLog::STL_ModLog_Debug,"%s\n", url.c_str());
        // stl_modlog(STL::STL_ModLog::STL_ModLog_Debug,"%s\n", json.c_str());

        std::string ret = "";
        CurlMemory chunk;

        CURL *curl = NULL;
        CURLcode res;
        char err_msg[CURL_ERROR_SIZE + 1] = {0};

        curl = curl_easy_init(); //对curl进行初始化
        curl_slist *pList = NULL;
        pList = curl_slist_append(pList, "Content-Type: application/json");
        // -----------------------------------------------------------------
        curl_easy_setopt(curl, CURLOPT_URL, req.url.c_str());
        curl_easy_setopt(curl, CURLOPT_POST, 1L);
        curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);
        curl_easy_setopt(curl, CURLOPT_HTTPHEADER, pList); //设置HTTP头
        curl_easy_setopt(curl, CURLOPT_TIMEOUT, req.timeout);
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)(&chunk));            //设置写数据的变量
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_function_callback); //设置写数据的回调函数
        curl_easy_setopt(curl, CURLOPT_POSTFIELDS, req.body.c_str());           //strJsonData需要是string类型
        curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, req.body.size());
        curl_easy_setopt(curl, CURLOPT_HEADER, 0L);
        curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)(&chunk));
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_function_callback);
        if (!m_dns.empty())
        {
            //https://curl.se/libcurl/c/CURLOPT_DNS_SERVERS.html
            curl_easy_setopt(curl, CURLOPT_DNS_SERVERS, m_dns.c_str());
        }
        // -----------------------------------------------------------------
        // curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);
        // curl_easy_setopt(curl, CURLOPT_URL, url);
        // curl_easy_setopt(curl, CURLOPT_POST, 1L);
        // curl_easy_setopt(curl, CURLOPT_HTTPHEADER, pList); //设置HTTP头
        // curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST");
        // curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
        // curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
        // curl_easy_setopt(curl, CURLOPT_HEADER, 0L);                             //启用时会将头文件的信息作为数据流输
        // curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);                     //允许重定向
        // curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)(&chunk));            //设置写数据的变量
        // curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_function_callback); //设置写数据的回调函数
        // curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, NULL); //设置写数据的回调函数
        // curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10);
        // curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, err_msg);

        // curl_easy_setopt(curl, CURLOPT_POSTFIELDS, json.c_str()); //strJsonData需要是string类型
        // curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, json.size());
        // -----------------------------------------------------------------
        // curl_easy_setopt(pCurl, CURLOPT_NOSIGNAL, 1L);
        // curl_easy_setopt(pCurl, CURLOPT_URL, url.c_str());
        // curl_easy_setopt(pCurl, CURLOPT_DNS_SERVERS, "8.8.8.8,8.8.4.4");
        // curl_easy_setopt(pCurl, CURLOPT_HTTPHEADER, pList);
        // //    curl_easy_setopt(pCurl, CURLOPT_HEADER ,1L);//输出带上消息头
        // curl_easy_setopt(pCurl, CURLOPT_CUSTOMREQUEST, method.c_str());
        // curl_easy_setopt(pCurl, CURLOPT_SSL_VERIFYHOST, 0L);
        // curl_easy_setopt(pCurl, CURLOPT_SSL_VERIFYPEER, 0L);
        // curl_easy_setopt(pCurl, CURLOPT_TRANSFER_ENCODING, 1L);
        // curl_easy_setopt(pCurl, CURLOPT_ACCEPT_ENCODING, "");
        // curl_easy_setopt(pCurl, CURLOPT_POSTFIELDSIZE, jsonBody.size());
        // curl_easy_setopt(pCurl, CURLOPT_POSTFIELDS, jsonBody.data());
        // curl_easy_setopt(pCurl, CURLOPT_VERBOSE, 0L); //TODO 发布前改为 0L
        // curl_easy_setopt(pCurl, CURLOPT_WRITEFUNCTION, writeMemoryCallback);
        // curl_easy_setopt(pCurl, CURLOPT_WRITEDATA, (void *)&response);
        // curl_easy_setopt(pCurl, CURLOPT_HEADERFUNCTION, writeMemoryCallback);
        // curl_easy_setopt(pCurl, CURLOPT_HEADERDATA, (void *)&resHeader);
        // curl_easy_setopt(pCurl, CURLOPT_CONNECTTIMEOUT, 10);
        // curl_easy_setopt(pCurl, CURLOPT_TIMEOUT, 10);
        // curl_easy_setopt(pCurl, CURLOPT_TCP_KEEPALIVE, 1L);
        // -----------------------------------------------------------------

        res = curl_easy_perform(curl); //发送(post)请求,res返回值0意味一切ok,非0代表错误发生
        curl_slist_free_all(pList);
        if (CURLE_OK != res)
        {
            int res_code = 0;
            res = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &res_code); //获得http返回的状态码
            curl_easy_cleanup(curl);
            if (req.isLog)
                stl_modlog(STL::STL_ModLog::STL_ModLog_Info, "\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n[POST] %s\n%s\n--------------------------------\ncurl_easy_perform failed: [CURLcode:%d][code:%d](err_msg:%s)\n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n", req.codeFile.c_str(), req.codeLine, __FUNCTION__, req.url.c_str(), req.body.c_str(), res, res_code, err_msg);
            return ret;
        }
        else
        {
            int res_code = 0;
            ret = std::string(chunk.response, chunk.size);
            res = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &res_code); //获得http返回的状态码
            if (req.isLog)
                stl_modlog(STL::STL_ModLog::STL_ModLog_Info, "\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n[POST] %s\n%s\n--------------------------------\n[code:%d][len:%zu]\n%s\n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n", req.codeFile.c_str(), req.codeLine, __FUNCTION__, req.url.c_str(), req.body.c_str(), res_code, chunk.size, ret.c_str() /*ret.substr(0, 1000).c_str()*/);
            curl_easy_cleanup(curl);
            return ret;
        }
        return ret;
    }
} // namespace STL
